﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO.MemoryMappedFiles;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using Red5.Frame.Utility.DataHandling.Compress;
using Red5.Frame.Utility.DataHandling.Encryption;

namespace Red5.Frame.Utility.DataBase.RedData.V1
{
    public class MemoryDb : IRedData, IDisposable
    {
        private readonly static ICompress COM = new DataHandling.Compress.V1.Handling();
        private readonly static IEncryption ENC = new DataHandling.Encryption.V1.Handling("RedEncryptionKey", "Red5VectorHere24");
        // 使用 volatile 确保多线程环境下的正确可见性
        private static volatile MemoryDb _instance;

        // 用于锁住写操作的并发字典
        private readonly ConcurrentDictionary<string, string> LOCK = new ConcurrentDictionary<string, string>();

        // 数据库文件路径
        private static string PATH;

        // 存储数据的并发字典
        private static ConcurrentDictionary<string, string> DATA;

        // 保存数据的次数阈值
        private static int SAVECOUNT = 300;

        // 保存数据的时间间隔（分钟）
        private static int SAVEMINUTE = 1;

        // 运行时间
        private static DateTime RUNTIME;

        // 定时器对象
        private static System.Timers.Timer TIMER;

        // 设置数据的计数器
        private long setCount = 0;

        // 控制写操作的信号量
        private SemaphoreSlim writeSemaphore = new SemaphoreSlim(1, 1);

        /// <summary>
        /// 私有构造函数，初始化 MemoryDb 实例。
        /// </summary>
        /// <param name="path">数据库文件路径。</param>
        /// <param name="count">保存数据的次数阈值，默认为 300。</param>
        /// <param name="minute">保存数据的时间间隔（分钟），默认为 1。</param>
        private MemoryDb(string path, int count = 300, int minute = 1)
        {
            // 验证并设置 SAVECOUNT 的合理范围
            if (SAVECOUNT < 100) SAVECOUNT = 300;
            else if (SAVECOUNT > 5000) SAVECOUNT = 5000;
            else SAVECOUNT = count;

            PATH = path;
            SAVEMINUTE = minute;
            DATA = new ConcurrentDictionary<string, string>();
            LOCK.TryAdd("_WriteUUID", ""); // 初始化锁

            RUNTIME = DateTime.Now; // 记录运行时间

            ReadLocalDataFile(); // 读取本地数据文件

            // 初始化定时器
            TIMER = new System.Timers.Timer((1000 * 60) * SAVEMINUTE);
            TIMER.Elapsed += OnTimerElapsed; // 定时器事件处理
            TIMER.Start(); // 启动定时器
        }

        /// <summary>
        /// 定时器事件触发时调用的方法，异步保存数据。
        /// </summary>
        /// <param name="sender">事件发送者。</param>
        /// <param name="e">事件参数。</param>
        private async void OnTimerElapsed(object sender, ElapsedEventArgs e)
        {
            await WriteLocalDataFileAsync(); // 异步保存数据
        }

        /// <summary>
        /// 读取本地数据文件。
        /// </summary>
        private void ReadLocalDataFile()
        {
            if (!File.Exists(PATH))
            {
                // 如果文件不存在，创建一个空文件
                WriteLocalDataFileAsync().GetAwaiter().GetResult();
            }

            try
            {
                // 使用内存映射文件加载数据
                using (MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile(PATH))
                {
                    using (MemoryMappedViewAccessor view = mmf.CreateViewAccessor())
                    {
                        // 获取文件大小
                        long fileSize = view.Capacity;

                        // 初始化一个字节数组来读取内存映射文件的内容
                        byte[] compressedData = new byte[fileSize];
                        view.ReadArray(0, compressedData, 0, (int)fileSize);

                        try
                        {
                            // 解压数据
                            COM.DecompressData(compressedData);
                        }
                        catch (Exception)
                        {
                            view.Dispose();
                            mmf.Dispose();
                            File.Delete(PATH);
                            ReadLocalDataFile();
                            return;
                        }
                        byte[] decryptedData = COM.Data;

                        // 解密数据
                        ENC.DecryptData(decryptedData);
                        byte[] dataBytes = ENC.Data;

                        // 反序列化数据
                        using (MemoryStream ms = new MemoryStream(dataBytes))
                        {
                            using (BinaryReader br = new BinaryReader(ms))
                            {
                                int itemCount = br.ReadInt32(); // 读取字典大小
                                for (int i = 0; i < itemCount; i++)
                                {
                                    string key = br.ReadString();
                                    string value = br.ReadString();
                                    DATA.TryAdd(key, value);
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.StackTrace);
            }
        }

        /// <summary>
        /// 异步写入本地数据文件。
        /// </summary>
        /// <returns>异步任务。</returns>
        private async Task WriteLocalDataFileAsync()
        {
            string uuid = Guid.NewGuid().ToString();
            if (LOCK.TryUpdate("_WriteUUID", uuid, ""))
            {
                await writeSemaphore.WaitAsync();
                try
                {
                    // 序列化数据到字节数组
                    using (var ms = new MemoryStream())
                    {
                        using (var bw = new BinaryWriter(ms))
                        {
                            bw.Write(DATA.Count); // 写入字典大小
                            foreach (var item in DATA)
                            {
                                bw.Write(item.Key);
                                bw.Write(item.Value);
                            }
                        }

                        byte[] dataBytes = ms.ToArray();

                        ENC.EncryptData(dataBytes);
                        // 加密数据
                        byte[] encryptedData = ENC.Data;
                        COM.CompressData(encryptedData);
                        // 压缩加密后的数据
                        byte[] compressedData = COM.Data;

                        // 如果文件已存在，先清空其内容
                        if (File.Exists(PATH))
                        {
                            File.WriteAllText(PATH, "");
                        }

                        // 创建内存映射文件，此时文件已清空，所以可以直接用数据大小
                        using (FileStream fs = new FileStream(PATH, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
                        {
                            using (MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile(fs, null, compressedData.LongLength, MemoryMappedFileAccess.ReadWrite, HandleInheritability.None, true))
                            {
                                using (MemoryMappedViewAccessor view = mmf.CreateViewAccessor())
                                {
                                    // 将压缩后的字节数组写入内存映射视图
                                    view.WriteArray(0, compressedData, 0, compressedData.Length);
                                }
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.StackTrace);
                }
                finally
                {
                    LOCK.TryUpdate("_WriteUUID", "", uuid);
                    writeSemaphore.Release();
                }
            }
        }

        /// <summary>
        /// 异步保存数据。
        /// </summary>
        public async void Save()
        {
            await WriteLocalDataFileAsync();
        }

        /// <summary>
        /// 获取指定键的值。
        /// </summary>
        /// <param name="key">键。</param>
        /// <returns>对应的值，如果未找到则返回空字符串。</returns>
        public string Get(string key)
        {
            if (string.IsNullOrEmpty(key)) throw new ArgumentException("Key 不能为空", nameof(key));
            if (DATA.ContainsKey(key))
            {
                return DATA[key];
            }
            return "";
        }

        /// <summary>
        /// 设置键值对。
        /// </summary>
        /// <param name="inKey">键。</param>
        /// <param name="inValue">值。</param>
        /// <returns>成功设置的标志。</returns>
        public int Set(string inKey, string inValue)
        {
            if (string.IsNullOrEmpty(inKey)) throw new ArgumentException("Key 不能为空", nameof(inKey));
            DATA.AddOrUpdate(inKey, inValue, (key, oldValue) => inValue);
            setCount++;

            if (setCount % SAVECOUNT == 0)
            {
                // 异步执行写操作
                Task.Run(async () =>
                {
                    await WriteLocalDataFileAsync();
                });
            }

            return 1;
        }

        /// <summary>
        /// 删除指定键的值。
        /// </summary>
        /// <param name="key">键。</param>
        /// <returns>删除成功的标志。</returns>
        public int Del(string key)
        {
            if (string.IsNullOrEmpty(key)) throw new ArgumentException("Key 不能为空", nameof(key));
            if (DATA.ContainsKey(key))
            {
                bool removed;
                // 尝试从 ConcurrentDictionary 中移除键值对
                removed = DATA.TryRemove(key, out _);

                return removed ? 1 : 0;
            }
            return 0;
        }

        /// <summary>
        /// 对指定键的数值进行增加。
        /// </summary>
        /// <param name="key">键。</param>
        /// <param name="amount">增加的数量，默认为 1。</param>
        /// <returns>增加后的值。</returns>
        public long Inc(string key, long amount = 1L)
        {
            if (string.IsNullOrEmpty(key)) throw new ArgumentException("Key 不能为空", nameof(key));

            // 尝试获取当前值
            string currentValueStr;
            if (DATA.TryGetValue(key, out currentValueStr))
            {
                // 解析当前值
                long currentValue;
                if (long.TryParse(currentValueStr, out currentValue))
                {
                    // 计算新值
                    long newValue = currentValue + amount;

                    // 更新值
                    DATA.AddOrUpdate(key, newValue.ToString(), (k, oldVal) => newValue.ToString());

                    // 增加计数器并检查是否需要保存
                    setCount++;
                    if (setCount % SAVECOUNT == 0)
                    {
                        Task.Run(async () => await WriteLocalDataFileAsync());
                    }

                    return newValue;
                }
                else
                {
                    throw new ArgumentException("无法解析当前值为数字", nameof(key));
                }
            }
            else
            {
                // 如果键不存在，则设置初始值
                DATA.TryAdd(key, amount.ToString());
                setCount++;
                if (setCount % SAVECOUNT == 0)
                {
                    Task.Run(async () => await WriteLocalDataFileAsync());
                }
                return amount;
            }
        }

        /// <summary>
        /// 对指定键的数值进行减少。
        /// </summary>
        /// <param name="key">键。</param>
        /// <param name="amount">减少的数量，默认为 1。</param>
        /// <returns>减少后的值；如果键不存在，则返回负数（表示递减失败）。</returns>
        public long Dec(string key, long amount = 1L)
        {
            if (string.IsNullOrEmpty(key)) throw new ArgumentException("Key 不能为空", nameof(key));

            // 尝试获取当前值
            string currentValueStr;
            if (DATA.TryGetValue(key, out currentValueStr))
            {
                // 解析当前值
                long currentValue;
                if (long.TryParse(currentValueStr, out currentValue))
                {
                    // 计算新值
                    long newValue = currentValue - amount;

                    // 更新值
                    DATA.AddOrUpdate(key, newValue.ToString(), (k, oldVal) => newValue.ToString());

                    // 增加计数器并检查是否需要保存
                    setCount++;
                    if (setCount % SAVECOUNT == 0)
                    {
                        Task.Run(async () => await WriteLocalDataFileAsync());
                    }

                    return newValue;
                }
                else
                {
                    throw new ArgumentException("无法解析当前值为数字", nameof(key));
                }
            }
            else
            {
                // 如果键不存在，则返回负数（表示递减失败）
                return -amount;
            }
        }

        /// <summary>
        /// 创建或获取 MemoryDb 的单例实例。
        /// </summary>
        /// <param name="path">数据库文件路径。</param>
        /// <param name="count">保存数据的次数阈值，默认为 300。</param>
        /// <param name="minute">保存数据的时间间隔（分钟），默认为 1。</param>
        /// <returns>MemoryDb 单例实例。</returns>
        public static MemoryDb CreateInstance(string path, int count = 300, int minute = 1)
        {
            if (string.IsNullOrEmpty(path)) throw new ArgumentException("数据库路径不能为空", nameof(path));

            // 验证路径有效性
            if (!Directory.Exists(Path.GetDirectoryName(path)))
            {
                throw new DirectoryNotFoundException($"指定的目录不存在: {path}");
            }

            if (!string.IsNullOrEmpty(PATH)) path = PATH;
            if (_instance == null)
            {
                lock (typeof(MemoryDb))
                {
                    if (_instance == null)
                    {
                        _instance = new MemoryDb(path, count, minute);
                    }
                }
            }
            return _instance;
        }

        /// <summary>
        /// 释放资源。
        /// </summary>
        public void Dispose()
        {
            TIMER.Stop();
            TIMER.Dispose();
        }

        /// <summary>
        /// 获取设置数据的计数器值。
        /// </summary>
        public long GetSetCount { get { return setCount; } }

        /// <summary>
        /// 获取自运行以来的总秒数。
        /// </summary>
        public double GetRunTime { get { return (DateTime.Now - RUNTIME).TotalSeconds; } }

        /// <summary>
        /// 获取写操作的状态信号量。
        /// </summary>
        public SemaphoreSlim GetWriteState { get { return writeSemaphore; } }
    }
}
